package analyzers;

import java.io.File;
import java.io.Serializable;

import util.Constants;
import weka.classifiers.Classifier;
import weka.classifiers.Evaluation;
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
import weka.filters.Filter;
import weka.filters.unsupervised.attribute.StringToWordVector;

public class TweetClassifier implements Serializable {
	private static final long serialVersionUID = -123455813150452885L;

	private static final int TOTAL_CAPACITY = 1000000;

	private static final String NOME_CONJUNTO_TREINAMENTO = "TreinamentoClassificadorTweets";
	private static final String NOME_CONJUNTO_TESTES = "TestesClassificadorTweets";

	private static final String ATTRIBUTE_TWEET = "Tweet";
	private static final String ATTRIBUTE_CLASS = "Class";

	private static final int TOTAL_ATRIBUTOS = 2;
	private static final int TOTAL_CLASSES = 2;

	private Instances trainningData;
	private Instances testingData;

	private StringToWordVector filter;

	private Classifier classifier;

	private String modelLocation;
	private boolean updated;

	public TweetClassifier(Classifier classifier, String modelLocation) {
		this.modelLocation = modelLocation;
		this.classifier = classifier;
		this.filter = initFilter();

		trainningData = initInstances(NOME_CONJUNTO_TREINAMENTO);
		testingData = initInstances(NOME_CONJUNTO_TESTES);
	}

	public void train(String tweet, String tweetClass) {
		Instance instance = createInstance(tweet, trainningData);
		instance.setClassValue(tweetClass);

		trainningData.add(instance);
		updated = false;
	}

	public void test(String tweet, String tweetClass) {
		Instance instance = createInstance(tweet, testingData);
		instance.setClassValue(tweetClass);

		testingData.add(instance);
	}

	public Evaluation evaluate() throws AnalyzerException {
		try {
			load();

			Instances filteredData = Filter.useFilter(testingData, filter);

			Evaluation evaluation = new Evaluation(filteredData);
			evaluation.evaluateModel(classifier, filteredData);
			return evaluation;
		} catch (Exception e) {
			e.printStackTrace();
			throw new AnalyzerException(e.getMessage());
		}
	}

	public String classify(String tweet) throws AnalyzerException {
		if (trainningData.numInstances() == 0) {
			throw new AnalyzerException(
					"Classificador nao treinado. Nao foi possivel classificar o tweet!");
		}

		load();

		Instances testset = trainningData.stringFreeStructure();
		Instance instance = createInstance(tweet, testset);
		return classify(instance);
	}

	private String classify(Instance instance) throws AnalyzerException {
		try {
			filter.input(instance);
			double predicted = classifier.classifyInstance(filter.output());
			return trainningData.classAttribute().value((int) predicted);
		} catch (Exception e) {
			e.printStackTrace();
			throw new AnalyzerException(e.getMessage());
		}
	}

	public void load() throws AnalyzerException {
		if (!updated) {
			try {
				filter.setInputFormat(trainningData);
				Instances filteredData = Filter.useFilter(trainningData,
						filter);
				classifier.buildClassifier(filteredData);
			} catch (Exception e) {
				throw new AnalyzerException(e.getMessage());
			}
			updated = true;
		}
	}

	public String getModelLocation() {
		return modelLocation;
	}

	public String getClassifierInfo() {
		return classifier.getClass().getName();
	}

	public String toString() {
		return classifier.toString();
	}

	private Instance createInstance(String text, Instances data) {
		Instance instance = new Instance(TOTAL_ATRIBUTOS);

		Attribute messageAtt = data.attribute(ATTRIBUTE_TWEET);
		instance.setValue(messageAtt, messageAtt.addStringValue(text));
		instance.setDataset(data);
		return instance;
	}

	private StringToWordVector initFilter() {
		StringToWordVector filter = new StringToWordVector();
		filter.setTokenizer(new SpellingTokenizer());
		filter.setLowerCaseTokens(true);
		filter.setStopwords(new File(Constants.STOPWORDS_FILE));
		return filter;
	}

	private Instances initInstances(String name) {
		FastVector classValues = new FastVector(TOTAL_CLASSES);
		classValues.addElement("feliz");
		classValues.addElement("triste");

		FastVector attributes = new FastVector(TOTAL_ATRIBUTOS);
		attributes
				.addElement(new Attribute(ATTRIBUTE_TWEET, (FastVector) null));
		attributes.addElement(new Attribute(ATTRIBUTE_CLASS, classValues));

		Instances instances = new Instances(name, attributes, TOTAL_CAPACITY);
		instances.setClassIndex(instances.numAttributes() - 1);
		return instances;
	}
}